<?php
// +----------------------------------------------------------------------
// | 数组工具库
// +----------------------------------------------------------------------

namespace helper\util;

trait Arr
{
    // {} 访问数组 $arr = [0,1,2];
    // echo $arr{0};php 7.4 前
    // echo $arr[0];php 7.4 后
    // 字符串偏移 $codechars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%";
    // echo $codechars{1};// php 7.4 前
    // echo str_split($codechars)[1];// php 7.4 后

    /**
     * 过滤数组元素前后空格 (支持多维数组)
     * @param array $array 要过滤的数组
     * @return array|string
     */
    function trim_array_element($array){
        if (!is_array($array))
            return trim($array);
        return array_map('trim_array_element', $array);
    }

    /**
     * 将数据库中查出的列表以指定的 值作为数组的键名，并以另一个值作为键值
     * @param $arr
     * @param $key_name
     * @return array
     */
    function convert_arr_kv($arr,$key_name,$value){
        $arr2 = array();
        foreach ($arr as $key => $val) {
            $arr2[$val[$key_name]] = $val[$value];
        }
        return $arr2;
    }

    function arr2str($info) {
        //删除空格，某些情况下字段的设置会出现换行和空格的情况
        if (is_array($info)) {
            if (array_key_exists('options', $info)) {
                $info['options'] = trim($info['options']);
            }
        }
        if ($info == '') return '';
        if (!is_array($info)){
            //删除反斜杠
            $string = stripslashes($info);
        }
        foreach ($info as $key => $val) {
            $string[$key] = stripslashes($val);
        }
        $setup = var_export($string, TRUE);
        return $setup;
    }

    /**
     * 对数组排序
     *
     * @param array $param 排序前的数组
     * @return array
     */
    public static function rsort(array &$param)
    {
        ksort($param);
        reset($param);
        return $param;
    }

    /**
     * 二维数组根据字段进行排序
     * @param array $arr  二维数组
     * @param string $field  指定字段
     * @param string $sort_order string SORT_ASC 按照上升顺序排序， SORT_DESC 按照下降顺序排序(具体请参考array_multisort官方定义)
     * @param string $sort_flags string 排序类型标志(具体请参考array_multisort官方定义)  如:忽略大小写:SORT_FLAG_CASE | SORT_STRING
     * @return array 排序后的数组
     */
    public function multiArraySort(array $arr, string $field, $sort_order = SORT_ASC, $sort_flags = SORT_REGULAR): array
    {
        // 异常判断
        if (!$arr || !$field) {
            return $arr;
        }

        // 将指定字段的值存进数组
        $tmp = [];
        foreach ($arr as $k => $v) {
            $tmp[$k] = $v[$field];
        }
        if (!$tmp) {
            return $arr;
        }

        // 调用php内置array_multisort函数
        array_multisort($tmp, $sort_order, $sort_flags, $arr);//constant($sort_flags)
        return $arr;
    }

    /**
     * 二维数组的排序
     * @param array $arr 需要排序的二维数组
     * @param string $field 以这个数组的值来排序
     * @return array            排序后的数组
     * @备注：默认升序，需要降序把$a和$b调换位置就好了
     */
    public function sort_array(array $arr, string $field): array
    {
        usort($arr, function ($a, $b) use ($field) {
            return ($a[$field] - $b[$field]);
        });
        return $arr;
    }

    /**
     * 取二维数组的某一项的最大值或者最小值
     * @param array $arr
     * @param string $field
     * @param bool $is_min
     * @return string
     */
    public function arraySearch(array $arr, string $field, bool $is_min = true)
    {
        // 判断是否是数组以及传过来的字段是否是空
        if (!is_array($arr) || !$field) {
            return false;
        }
        // 用一个空数组来承接字段
        $temp = array();
        foreach ($arr as $val) {
            $temp[] = $val[$field];
        }
        return $is_min ? min($temp) : max($temp);
    }

    /**
     * 查询二维数组中value等于某个值的
     * @param $value
     * @param $array
     * @return array
     */
    function multiArrayEqValue($array, $key, $value): array
    {
        return array_filter($array, function($arr) use ($key, $value) {
            return $arr[$key] == $value;
        });
    }

    /**
     * 查询二维数组中value等于某个值的
     * @param $value
     * @param $array
     * @return bool
     */
    function deep_in_array($value, $array): bool
    {
        foreach ($array as $item) {
            if (!is_array($item)) {
                if ($item == $value) {
                    return true;
                } else {
                    continue;
                }
            }
            if (in_array($value, $item)) {
                return true;
            } else if ($this->deep_in_array($value, $item)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 通知邮件/通知消息 内容转换函数
     *
     * @param string $message 内容模板
     * @param array $param 内容参数数组
     * @return string 通知内容
     */
    public static function replace_text(string $message, array $param): string
    {
        foreach ($param as $k => $v) {
            $message = str_replace('${' . $k . '}', $v, $message);
        }
        return $message;
    }

    /**
     * 通过a.b.c 读取$arr['a']['b']['c']
     * @param array $config
     * @param string $name
     * @param string $delimiter
     * @return mixed|string
     */
    function readArrVal(array $config, string $name = '', string $delimiter = '.')
    {
        if (!$name) {
            return $config;
        }
        // 按.拆分成多维数组进行判断
        $name = explode($delimiter, $name);
        foreach ($name as $val) {
            $config = $config[$val] ?? '';
        }
        return $config;
    }

    /**
     * 返回多层栏目
     * @param array $data
     * @param int $pid
     * @param string $html
     * @param int $level
     * @return array
     */
    public static function tree(array $data, int $pid = 0, string $html = '|--', int $level = 0)
    {
        static $arr = [];
        foreach ($data as $val) {
            if ($pid == $val['parent_id']) {
                $val['html']  = str_repeat($html, $level);
                $val['level'] = $level + 1;
                $arr[]        = $val;
                self::tree($data, $val['id'], $html, $val['level']);
            }
        }
        return $arr;
    }

    /**
     * [递归] 通过父类ID获取所有的子类,在进行删除栏目的时候，需要删除当前栏目下的所有子栏目
     * @param array $data [['id'=>1,'parent_id'=>0],['id'=>2,'parent_id'=>1]]
     * @param int $pid
     * @param string $field
     * @param string $id_name
     * @param string $pid_name
     * @param bool $omit 省略尾部$field
     * @return array
     */
    public static function subTree(array $data, int $pid = 0, string $field = 'children', string $id_name = 'id', string $pid_name = 'parent_id', $omit = false)
    {
        $arr = [];
        foreach ($data as $item) {
            if ($pid == $item[$pid_name]) {
                $child = self::subTree($data, $item[$id_name], $field, $id_name, $pid_name, $omit);
                if ($child || $omit) {
                    $item[$field] = $child;
                }
                $arr[] = $item;
            }
        }
        return $arr;
    }

    /**
     * [递归] 通过子类ID获取所有的父类
     * @param array $data [['id'=>1,'parent_id'=>0],['id'=>2,'parent_id'=>1]]
     * @param int $cid
     * @param string $field
     * @param string $id_name
     * @param string $pid_name
     * @return array
     */
    public static function supTree(array $data, int $cid, string $field = 'children', string $id_name = 'id', string $pid_name = 'parent_id')
    {
        $arr = [];
        foreach ($data as $item) {
            if ($item[$id_name] == $cid) {
                $item[$field] = self::supTree($data, $item[$pid_name], $field, $id_name, $pid_name);
                $arr[]        = $item;
            }
        }
        return $arr;
    }

    /**
     * 获取所有的分类的ID
     * @param array $data [['id'=>1,'parent_id'=>0],['id'=>2,'parent_id'=>1]]
     * @return array
     */
    public static function children(array $data)
    {
        $ids        = array_column($data, 'id');//返回输入数组中某个单一列的值
        $parent_ids = array_column($data, 'parent_id');
        $sub        = array_diff($ids, $parent_ids);//获取所有的子分类的ID
        $sup        = array_diff($ids, $sub);//获取所有的父分类的ID
        return compact("sub", "sup");//创建包含变量名和它们的值的数组
    }

    /**
     * 检测是否有子栏目
     * @param $data array 栏目数据
     * @param $cid int 要判断的栏目cid
     * @param string $fieldPid 父id表字段名
     * @return bool
     */
    public static function hasChild($data, $cid, $fieldPid = 'parent_id')
    {
        foreach ($data as $d) {
            if ($d[$fieldPid] == $cid) return true;
        }
        return false;
    }

    /**
     * 二维数组查询符合字段和字段值的数组集合
     * @param array $data 待过滤数组
     * @param string $field 要查找的字段
     * @param string $value 要查找的字段值
     * @return array
     */
    function array_multi_filter(array $data, string $field, string $value): array
    {
        return array_filter($data, function ($row) use ($field, $value) {
            if (isset($row[$field])) {
                return $row[$field] == $value;
            }
            return [];
        });
    }

    /**
     * 二维数组差集
     * @param array $array1 要被对比的数组
     * @param array $array2 和数组进行比较
     * @return array
     */
    function array_multi_diff(array $array1, array $array2): array
    {
        return array_filter($array1, function ($v) use ($array2) {
            return !in_array($v, $array2);
        });
    }

    // PHP二维数组（或任意维数组）转换成一维数组的方法汇总
//array_filter() - 用回调函数过滤数组中的单元
//array_reduce() - 用回调函数迭代地将数组简化为单一的值
//array_walk() - 使用用户自定义函数对数组中的每个元素做回调处理 返回: true或者false；需要return返回 场景: 用来修改数组中元素，也可以新增修改。可替换foreach
//array_map() - 为数组的每个元素应用回调函数 返回: 处理后的数组；需要在传入参数值加&引用符号.！ 场景：主要是根据某个/某几个数组产生一个新的数组，常应用于递归效果。
    /**
     * 多维数组转一维数组
     * @param $arr
     * @return array
     */
    function multi_array_to_one1($arr)
    {
        return array_reduce($arr, function ($result, $value) {
            return array_merge($result, array_values($value));
        }, []);
    }

    /**
     * 多维数组转一维数组
     * @param $arr
     * @return array
     */
    function multi_array_to_one2($arr)
    {
        $result = [];
        array_walk_recursive($arr, function ($value) use (&$result) {
            array_push($result, $value);
        });
        return $result;
    }

    /**
     * 多维数组转一维数组
     * @param $arr
     * @return array
     */
    function multi_array_to_one3($arr)
    {
        $result = [];
        array_map(function ($value) use (&$result) {
            $result = array_merge($result, array_values($value));
        }, $arr);
        return $result;
    }

    /**
     * 多维数组转一维数组
     * @param $arr
     * @return array
     */
    function multi_array_to_one4($arr)
    {
        $result = [];
        array_walk($arr, function ($value) use (&$result) {
            $result = array_merge($result, array_values($value));
        });
        return $result;
    }

    /**
     * 多维数组转一维数组
     * @param array $multi
     * @return array
     */
    function multi_array_to_one5($multi)
    {
        $arr = array();
        foreach ($multi as $key => $val) {
            if (is_array($val)) {
                $arr = array_merge($arr, multi_array_to_one5($val));
            } else {
                $arr[] = $val;
            }
        }
        return $arr;
    }

    /**
     * 三维数组转二维数组
     * @param array $data 数组
     * @return array
     */
    function multipleArrayConvert($data)
    {
        $arr = [];
        foreach ($data as $value) {
            foreach ($value as $v) {
                $arr[] = $v;
            }
        }
        return $arr;
    }

    /**
     * 数组分页
     * 用此函数之前要先将数据库里面的所有数据按一定的顺序查询出来存入数组中
     * @param array $array 查询出来的所有数组
     * @param int $count 每页多少条数据
     * @param int $page 当前第几页
     * @param bool $order true - 不变     false- 反序
     */
    public function arrayPage(array $array, int $page = 1, int $count = 10, bool $order = false): array
    {
        global $total; //定全局变量
        $page  = $page ?? 1; //判断当前页面是否为空 如果为空就表示为第一页面
        $start = ($page - 1) * $count; //计算每次分页的开始位置
        if ($order) {
            $array = array_reverse($array);
        }
        $total = ceil(count($array) / $count); //计算总页面数
        return array_slice($array, $start, $count);  //返回查询数据
    }

    /**
     * 是否为关联数组
     *
     * @param array $arr 数组
     * @return bool
     */
    public static function isAssoc($arr)
    {
        return array_keys($arr) !== range(0, count($arr) - 1);
    }

    /**
     * 不区分大小写的in_array实现
     *
     * @param $value
     * @param $array
     * @return bool
     */
    public static function in($value, $array)
    {
        return in_array(strtolower($value), array_map('strtolower', $array));
    }

////提取多维数组指定列的方法
//$arr = array(
//    '0' => array('id' => 1, 'name' => 'name1'),
//    '1' => array('id' => 2, 'name' => 'name2'),
//    '2' => array('id' => 3, 'name' => 'name3'),
//    '3' => array('id' => 4, 'name' => 'name4'),
//    '4' => array('id' => 5, 'name' => 'name5'),
//);
////1、array_column()
//$name_list = array_column($arr, 'name');
//
////2、array_walk() 使用用户自定义函数对数组中的每个元素做回调处理
//$name_list = array();
//array_walk($arr, function($value, $key) use (&$name_list ){
//    $name_list [] = $value['name'];
//});
//
////3、array_map()函数和array_walk() 作用类似，为数组的每个元素应用回调函数 可以用来代替foreach
//$name_list = array();
//array_map(function($value) use (&$name_list){
//    $name_list[] = $value['name'];
//}, $arr);
//
////4、foreach循环遍历方法 相对上面的方法效率稍微低一些
//$name_list = array();
//foreach ($arr as $value) {
//    $name_list[] = $value['name'];
//}
//
////5、array_map变种把$arr数组的每一项值的开头值移出，并获取移除的值作为新数组。注意此时新数组$name_list的键仍是原数组$arr的键
//$name_list = array_map('array_shift', $arr);
////注意：该功能会获取$arr中的 id 列，而不是name 列。另外，如果需要获取二维数组每一项的开头列或结尾列，也可以这样做：
//
//$name_list = array_map('reset', $arr);
//$name_list = array_map('end', $arr);
//
////这三个变种方法作用比较局限，仅在获取第一列或最后一列的时候有用，在复杂的数组中就难以发挥作用了
///


    //$a = [
//[
//'name'   => 'a',
//'num'    => 1,
//'amount' => 1,
//],
//[
//'name'   => 'b',
//'num'    => 1,
//'amount' => 1,
//],
//];
//
//$b = [
//[
//'name'   => 'a',
//'num'    => 2,
//'amount' => 2,
//],
//[
//'name'   => 'c',
//'num'    => 2,
//'amount' => 2,
//],
//];
//
//$c = [
//[
//'name'   => 'b',
//'num'    => 2,
//'amount' => 2,
//],
//[
//'name'   => 'c',
//'num'    => 2,
//'amount' => 2,
//],
//];
//// 数组合并,一部分值相加
//// 1) 数组合并, [[],[]]
//$merge_arr = array_merge($a, $b, $c);
//
//// 2) 相同的键组成数组, [['a']=>[[],[]]]
//$old_arr = [];
//foreach ($merge_arr as $v) {
//$old_arr[$v['name']][] = $v;
//}
//
//// 3) 相同的键数内值相加
//$new_arr = [];
//foreach ($old_arr as $va) {
//    $num    = 0;
//    $amount = 0;
//    $row    = [];
//    for ($i = 0; $i < count($va); $i++) {
//        $name   = $va[0]['name'];
//        $num    += $va[$i]['num'];
//        $amount += $va[$i]['amount'];
//        $row    = ['name' => $name, 'num' => $num, 'amount' => $amount];
//    }
//    $new_arr[] = $row;
//}
//print_r($new_arr);

    /**
     * 二维数组分组
     * @param array $arr 数组
     * @param string $key 键
     * @return array
     */
    public static function group(array $arr, string $key): array
    {
        $grouped = [];
        foreach ($arr as $value) {
            $grouped[$value[$key]][] = $value;
        }
        // Recursively build a nested grouping if more parameters are supplied
        // Each grouped array value is grouped according to the next sequential key
        if (func_num_args() > 2) {
            $args = func_get_args();
            foreach ($grouped as $key => $value) {
                $params        = array_merge([$value], array_slice($args, 2, func_num_args()));
                $grouped[$key] = call_user_func_array('array_group_by', $params);
            }
        }
        return $grouped;
    }

    /**
     * 二维数组分组统计
     * @param array $arr
     * @param string $key
     * @param string $count
     * @return array
     */
    public static function groupStatistics(array $arr, string $key, string $count = 'count'): array
    {
        $out = [];
        foreach ($arr as $row) {
            if (!isset($out[$row[$key]])) {
                $out[$row[$key]] = [$key => $row[$key], $count => 0];
            }
            $out[$row[$key]][$count]++;
        }
        return $out;
    }

    /**
     * 分组统计
     * @param array $array
     * @param string $column_key
     * @param string $index_key
     * @return array
     */
    public static function groupBy(array $array, string $column_key, string $index_key): array
    {
        $arr = array_column($array, $column_key, $index_key);
        return array_count_values($arr);
    }
}
